package com.ccrfid.rmc6.service.alarm;

import com.ccrfid.rmc6.appconfig.exception.pojo.AppException;
import com.ccrfid.rmc6.appconfig.exception.pojo.CustomErrors;
import com.ccrfid.rmc6.entity.alarm.AlarmNotifyRecordEntity;
import com.ccrfid.rmc6.entity.alarm.AlarmNotifyStrategyEntity;
import com.ccrfid.rmc6.entity.alarm.AlarmRecordEntity;
import com.ccrfid.rmc6.entity.alarm.pojo.AlarmLevelDto;
import com.ccrfid.rmc6.entity.alarm.pojo.AlarmTypeDto;
import com.ccrfid.rmc6.entity.alarm.pojo.OrgDto;
import com.ccrfid.rmc6.entity.alarm.pojo.PersonnelDto;
import com.ccrfid.rmc6.entity.personnel.PersonnelEntity;
import com.ccrfid.rmc6.pojo.Constants;
import com.ccrfid.rmc6.pojo.dto.alarm.AlarmNotifyRecordDto;
import com.ccrfid.rmc6.repository.alarm.AlarmNotifyRecordRepository;
import com.ccrfid.rmc6.repository.alarm.AlarmNotifyStrategyRepository;
import com.ccrfid.rmc6.repository.search.SearchCriteria;
import com.ccrfid.rmc6.service.personnel.PersonnelService;
import com.ccrfid.rmc6.util.JsonUtil;
import com.google.common.eventbus.EventBus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import javax.ws.rs.core.Response;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@Service
@Transactional
public class AlarmNotifyServiceImpl implements AlarmNotifyService {
    private static final Logger logger = LoggerFactory.getLogger(AlarmNotifyServiceImpl.class);
    @Autowired
    private AlarmNotifyRecordRepository alarmNotifyRecordRepository;
    @Autowired
    private AlarmNotifyStrategyRepository alarmNotifyStrategyRepository;
    @Autowired
    private PersonnelService personnelService;
    @Autowired
    private EventBus eventBus;

    @Override
    public Page<AlarmNotifyStrategyEntity> getAlarmNotifyStrategyPage(SearchCriteria criteria, Pageable pageable) {
        return alarmNotifyStrategyRepository.search(criteria, pageable);
    }

    @Override
    public AlarmNotifyStrategyEntity getAlarmNotifyStrategyById(Long strategyId) {
        return alarmNotifyStrategyRepository.getById(strategyId);
    }

    @Override
    public AlarmNotifyStrategyEntity createAlarmNotifyStrategy(AlarmNotifyStrategyEntity entity) throws AppException {

        checkParam(entity);

        return alarmNotifyStrategyRepository.save(entity);
    }

    private void checkParam(AlarmNotifyStrategyEntity entity) throws AppException {
        if (entity.getStrategyName() == null) {
            throw new AppException(Response.Status.BAD_REQUEST.getStatusCode(),
                    CustomErrors.alarm_notify_strategy_name_is_null.getCode(),
                    CustomErrors.alarm_notify_strategy_name_is_null.getReason(), "");
        }
        if (entity.getEnabled() == null) {
            throw new AppException(Response.Status.BAD_REQUEST.getStatusCode(),
                    CustomErrors.enabled_is_null.getCode(), CustomErrors.enabled_is_null.getReason(), "");
        }
        if (entity.getAlarmLevels() == null || entity.getAlarmLevels().size() == 0) {
            throw new AppException(Response.Status.BAD_REQUEST.getStatusCode(),
                    CustomErrors.alarm_level_is_null.getCode(), CustomErrors.alarm_level_is_null.getReason(), "");
        }
        if (entity.getAlarmTypes() == null || entity.getAlarmTypes().size() == 0) {
            throw new AppException(Response.Status.BAD_REQUEST.getStatusCode(),
                    CustomErrors.alarm_type_is_null.getCode(), CustomErrors.alarm_type_is_null.getReason(), "");
        }
    }

    @Override
    public AlarmNotifyStrategyEntity updateAlarmNotifyStrategy(Long strategyId, AlarmNotifyStrategyEntity entity)
            throws AppException {
        if (!alarmNotifyStrategyRepository.existsById(strategyId)) {
            throw new AppException(Response.Status.NOT_FOUND.getStatusCode(),
                    CustomErrors.resource_not_found.getCode(), CustomErrors.resource_not_found.getReason(), "");
        }
        if (!strategyId.equals(entity.getId())) {
            throw new AppException(Response.Status.BAD_REQUEST.getStatusCode(),
                    CustomErrors.different_args.getCode(), CustomErrors.different_args.getReason(), "");
        }
        checkParam(entity);

        return alarmNotifyStrategyRepository.save(entity);
    }

    @Override
    public Boolean deleteAlarmNotifyStrategies(List<Long> idList) throws AppException {
        if (idList == null) {
            throw new AppException(Response.Status.BAD_REQUEST.getStatusCode(),
                    CustomErrors.param_is_null.getCode(), CustomErrors.param_is_null.getReason(), "");
        }
        for (Long strategyId : idList) {
            if (alarmNotifyStrategyRepository.existsById(strategyId)) {
                alarmNotifyStrategyRepository.deleteById(strategyId);
            }
        }
        return true;
    }

    @Override
    public List<AlarmNotifyStrategyEntity> getAlarmNotifyStrategyList() {
        return alarmNotifyStrategyRepository.getAllByEnabled(Constants.ENABLE_STATUS.ENABLE.id);
    }

    @Override
    public AlarmNotifyRecordEntity createAlarmNotifyRecord(AlarmNotifyRecordEntity entity) {
        return alarmNotifyRecordRepository.save(entity);
    }

    @Override
    public void postAlarmNotify(Long personnelId, AlarmRecordEntity recordEntity) {
        if (logger.isTraceEnabled()) {
            logger.trace("postAlarmNotify personnelId: {}", personnelId);
        }
        long startTime = System.currentTimeMillis();
        List<AlarmNotifyStrategyEntity> alarmNotifyStrategyList = getAlarmNotifyStrategyList();

        if (alarmNotifyStrategyList != null && alarmNotifyStrategyList.size() > 0) {
            Set<Long> alarmPersonnelIdSet = new HashSet<>();
            alarmPersonnelIdSet.add(personnelId);
            if (recordEntity.getPersonnelIds() != null) {
                Long[] ids = JsonUtil.INSTANCE.readValue(recordEntity.getPersonnelIds(), Long[].class);
                for (Long id : ids) {
                    alarmPersonnelIdSet.add(id);
                }
            }
            //获取告警需要通知到的人员/警员
            Set<PersonnelDto> notifyPersonnelSet = new HashSet<>();
            for (AlarmNotifyStrategyEntity notifyStrategy : alarmNotifyStrategyList) {
                for (Long alarmPersonnelId : alarmPersonnelIdSet) {
                    PersonnelEntity personnelEntity = personnelService.getPersonnelById(alarmPersonnelId);
                    if (personnelEntity != null
                            && matchAlarmNotifyStrategy(recordEntity.getAlarmLevel(), recordEntity.getAlarmType(),
                            personnelEntity.getOrgId(), personnelEntity.getId(), notifyStrategy)) {
                        //某条告警信息满足某个告警策略时,记录告警需要通知到的人员
                        for (PersonnelDto personnelDto : notifyStrategy.getAlarmNotifyPersonnels()) {
                            notifyPersonnelSet.add(personnelDto);
                        }
                        break;
                    }
                }
            }
            //将告警通知到特定警员
            if (notifyPersonnelSet.size() > 0) {
                for (PersonnelDto personnelDto : notifyPersonnelSet) {
                    AlarmNotifyRecordEntity notifyRecordEntity =
                            alarmNotifyRecordRepository.getByNotifyPersonnelIdAndAlarmRecordId(
                                    personnelDto.getPersonnelId(), recordEntity.getId());
                    if (notifyRecordEntity != null) {
                        AlarmNotifyRecordDto notifyRecordDto = new AlarmNotifyRecordDto(notifyRecordEntity, recordEntity);
                        eventBus.post(notifyRecordDto);

                    } else {
                        notifyRecordEntity = new AlarmNotifyRecordEntity();
                        notifyRecordEntity.setNotifyPersonnelId(personnelDto.getPersonnelId());
                        notifyRecordEntity.setAlarmRecordId(recordEntity.getId());
                        notifyRecordEntity.setRead(0);
                        notifyRecordEntity = alarmNotifyRecordRepository.save(notifyRecordEntity);
                        AlarmNotifyRecordDto notifyRecordDto = new AlarmNotifyRecordDto(notifyRecordEntity, recordEntity);
                        eventBus.post(notifyRecordDto);

                    }
                }
            }
        }
        long stopTime = System.currentTimeMillis();
        if (logger.isTraceEnabled()) {
            logger.trace("postAlarmNotify cost: {}ms, personnelId: {}", stopTime - startTime, personnelId);
        }
    }

    private boolean matchAlarmNotifyStrategy(Integer alarmLevel, Integer alarmType, Long orgId, Long personnelId,
                                             AlarmNotifyStrategyEntity alarmNotifyStrategy) {
        return (matchAlarmLevel(alarmLevel, alarmNotifyStrategy)
                && matchAlarmType(alarmType, alarmNotifyStrategy)
                && matchAlarmPersonnelOrg(orgId, alarmNotifyStrategy))
                ||
                (matchAlarmLevel(alarmLevel, alarmNotifyStrategy)
                        && matchAlarmType(alarmType, alarmNotifyStrategy)
                        && matchAlarmPersonnel(personnelId, alarmNotifyStrategy));
    }

    private boolean matchAlarmLevel(Integer alarmLevel, AlarmNotifyStrategyEntity alarmNotifyStrategy) {
        List<AlarmLevelDto> alarmLevels = alarmNotifyStrategy.getAlarmLevels();
        if (alarmLevels != null && alarmLevels.size() > 0) {
            for (AlarmLevelDto dto : alarmLevels) {
                if (dto.getAlarmLevelId().equals(alarmLevel)) {
                    return true;
                }
            }
        }
        return false;
    }

    private boolean matchAlarmType(Integer alarmType, AlarmNotifyStrategyEntity alarmNotifyStrategy) {
        List<AlarmTypeDto> alarmTypes = alarmNotifyStrategy.getAlarmTypes();
        if (alarmTypes != null && alarmTypes.size() > 0) {
            for (AlarmTypeDto dto : alarmTypes) {
                if (dto.getAlarmTypeId().equals(alarmType)) {
                    return true;
                }
            }
        }
        return false;
    }

    private boolean matchAlarmPersonnelOrg(Long orgId, AlarmNotifyStrategyEntity alarmNotifyStrategy) {
        List<OrgDto> alarmPersonnelOrgs = alarmNotifyStrategy.getAlarmPersonnelOrgs();
        if (alarmPersonnelOrgs != null && alarmPersonnelOrgs.size() > 0) {
            for (OrgDto dto : alarmPersonnelOrgs) {
                if (dto.getOrgId().equals(orgId)) {
                    return true;
                }
            }
        }
        return false;
    }

    private boolean matchAlarmPersonnel(Long personnelId, AlarmNotifyStrategyEntity alarmNotifyStrategy) {
        List<PersonnelDto> alarmPersonnels = alarmNotifyStrategy.getAlarmPersonnels();
        if (alarmPersonnels != null && alarmPersonnels.size() > 0) {
            for (PersonnelDto dto : alarmPersonnels) {
                if (dto.getPersonnelId().equals(personnelId)) {
                    return true;
                }
            }
        }
        return false;
    }

    @Override
    public List<AlarmNotifyRecordEntity> getAllByRecordId(Long alarmRecordId) {
        return alarmNotifyRecordRepository.getAllByAlarmRecordId(alarmRecordId);
    }

    @Override
    public AlarmNotifyRecordEntity getAlarmNotifyRecordById(Long alarmNotifyRecordId) {
        return alarmNotifyRecordRepository.getById(alarmNotifyRecordId);
    }
}
